﻿using Microsoft.VisualStudio.Modeling;
using System;
using System.Data;

namespace Project.Co_DataTransformationPrototype
{
    partial class EntityAttribute
    {
        partial class TypePropertyHandler
        {
            protected override void OnValueChanged(EntityAttribute element, SqlDbType oldValue, SqlDbType newValue)
            {
                if (oldValue.Equals(newValue))
                    return;

                if (element.isPrimaryKey || element.isForeignKey)
                    throw new ModelingException("Changing the data type of a key-attribute it not allowed. First remove the key-constraint (not the attribute itself) then change the data type.");

                if (isSupportedConversion(oldValue, newValue))
                    base.OnValueChanged(element, oldValue, newValue);
                else
                    throw new NotSupportedException(String.Format("The conversion of attribute type '{0}' to type '{1}' is not supported.", oldValue, newValue));
            }

            /// <summary>
            /// Determines if the given data type change is supported
            /// Note:Based on the allowed column data-type conversion for SQL Server
            /// </summary>
            /// <param name="oldValue"></param>
            /// <param name="newValue"></param>
            /// <returns></returns>
            private bool isSupportedConversion(SqlDbType oldValue, SqlDbType newValue)
            {
                bool isSupported = true;

                #region Binary & VarBinary Conversion Validation
                if (oldValue.Equals(SqlDbType.Binary) || oldValue.Equals(SqlDbType.VarBinary))
                {
                    switch (newValue)
                    {
                        //case SqlDbType.Float:         // Custom conversion
                        //case SqlDbType.Real:          // Custom conversion
                        case SqlDbType.Text:
                        case SqlDbType.NText:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region NChar & NVarChar Conversion Validation
                else if (oldValue.Equals(SqlDbType.NChar) || oldValue.Equals(SqlDbType.NVarChar))
                {
                    switch(newValue)
                    {
                        case SqlDbType.Image:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Datetime & small datatime Conversion Validation
                else if (oldValue.Equals(SqlDbType.DateTime) || oldValue.Equals(SqlDbType.SmallDateTime))
                {
                    switch (newValue)
                    {
                        case SqlDbType.UniqueIdentifier:
                        case SqlDbType.Image:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Date Conversion Validation
                else if (oldValue.Equals(SqlDbType.Date))
                {
                    switch (newValue)
                    {
                        case SqlDbType.Time:
                        case SqlDbType.Decimal:
                        case SqlDbType.Float:
                        case SqlDbType.Real:
                        case SqlDbType.BigInt:
                        case SqlDbType.Int:
                        case SqlDbType.SmallInt:
                        case SqlDbType.TinyInt:
                        case SqlDbType.Money:
                        case SqlDbType.SmallMoney:
                        case SqlDbType.Bit:
                        case SqlDbType.Timestamp:
                        case SqlDbType.UniqueIdentifier:
                        case SqlDbType.Image:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Time conversion Validation
                else if (oldValue.Equals(SqlDbType.Time))
                {
                    switch (newValue)
                    {
                        case SqlDbType.Date:
                        case SqlDbType.Decimal:
                        case SqlDbType.Float:
                        case SqlDbType.Real:
                        case SqlDbType.BigInt:
                        case SqlDbType.Int:
                        case SqlDbType.SmallInt:
                        case SqlDbType.TinyInt:
                        case SqlDbType.Money:
                        case SqlDbType.SmallMoney:
                        case SqlDbType.Bit:
                        case SqlDbType.Timestamp:
                        case SqlDbType.UniqueIdentifier:
                        case SqlDbType.Image:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region DateTimeOffset & DateTime2 Conversion Validation
                else if (oldValue.Equals(SqlDbType.DateTimeOffset) || oldValue.Equals(SqlDbType.DateTime2))
                {
                    switch (newValue)
                    {
                        case SqlDbType.Decimal:
                        case SqlDbType.Float:
                        case SqlDbType.Real:
                        case SqlDbType.BigInt:
                        case SqlDbType.Int:
                        case SqlDbType.SmallInt:
                        case SqlDbType.TinyInt:
                        case SqlDbType.Money:
                        case SqlDbType.SmallMoney:
                        case SqlDbType.Bit:
                        case SqlDbType.Timestamp:
                        case SqlDbType.UniqueIdentifier:
                        case SqlDbType.Image:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Decimal, Bigint, int, smallint, tinyint, money, smallmoney and bit Convertion Validation
                else if (oldValue.Equals(SqlDbType.Decimal) || oldValue.Equals(SqlDbType.BigInt) || 
                    oldValue.Equals(SqlDbType.Int) || oldValue.Equals(SqlDbType.SmallInt) || oldValue.Equals(SqlDbType.TinyInt) || 
                    oldValue.Equals(SqlDbType.Money) || oldValue.Equals(SqlDbType.SmallMoney) || oldValue.Equals(SqlDbType.Bit))
                {
                    switch (newValue)
                    {
                        case SqlDbType.UniqueIdentifier:        // Custom conversion
                            if (oldValue.Equals(SqlDbType.Int) || oldValue.Equals(SqlDbType.BigInt))
                                isSupported = true;            // TODO: There has to be a better way to do this
                            break;
                        case SqlDbType.Date:
                        case SqlDbType.Time:
                        case SqlDbType.DateTimeOffset:
                        case SqlDbType.DateTime2:
                        case SqlDbType.Image:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Float & Real Conversion Validation
                else if (oldValue.Equals(SqlDbType.Float) || oldValue.Equals(SqlDbType.Real))
                {
                    switch (newValue)
                    {
                        case SqlDbType.Date:
                        case SqlDbType.Time:
                        case SqlDbType.DateTimeOffset:
                        case SqlDbType.DateTime2:
                        case SqlDbType.Timestamp:
                        case SqlDbType.UniqueIdentifier:
                        case SqlDbType.Image:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Timestamp Conversion Validation
                else if (oldValue.Equals(SqlDbType.Timestamp))
                {
                    switch (newValue)
                    {
                        case SqlDbType.NChar:
                        case SqlDbType.NVarChar:
                        case SqlDbType.Date:
                        case SqlDbType.Time:
                        case SqlDbType.DateTimeOffset:
                        case SqlDbType.DateTime2:
                        case SqlDbType.Float:
                        case SqlDbType.Real:
                        case SqlDbType.UniqueIdentifier:
                        case SqlDbType.NText:
                        case SqlDbType.Text:
                        case SqlDbType.Variant:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region UniqueIdentifier Conversion Validation
                else if (oldValue.Equals(SqlDbType.UniqueIdentifier))
                {
                    // Change Perspective from negation to (the opposite of negation)  :D
                    isSupported = false;
                    switch (newValue)
                    {
                        case SqlDbType.Binary:
                        case SqlDbType.VarBinary:
                        case SqlDbType.Char:
                        case SqlDbType.VarChar:
                        case SqlDbType.NChar:
                        case SqlDbType.NVarChar:
                        case SqlDbType.Variant:
                            isSupported = true; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Image
                else if (oldValue.Equals(SqlDbType.Image))
                {
                    // Change Perspective from negation to (the opposite of negation)
                    isSupported = false;
                    switch (newValue)
                    {
                        case SqlDbType.Binary:
                        case SqlDbType.VarBinary:
                        case SqlDbType.Timestamp:
                            isSupported = true; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region Text & NText
                else if (oldValue.Equals(SqlDbType.Text) || oldValue.Equals(SqlDbType.NText))
                {
                    // Change Perspective from negation to (the opposite of negation)
                    isSupported = false;
                    switch (newValue)
                    {
                        case SqlDbType.Char:
                        case SqlDbType.VarChar:
                        case SqlDbType.NChar:
                        case SqlDbType.NVarChar:
                        case SqlDbType.Text:
                        case SqlDbType.NText:
                        case SqlDbType.Xml:
                            isSupported = true; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region (sql_)Variant Conversion Validation
                else if (oldValue.Equals(SqlDbType.Variant))
                {
                    switch (newValue)
                    {
                        case SqlDbType.Timestamp:
                        case SqlDbType.Image:
                        case SqlDbType.Text:
                        case SqlDbType.NText:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = false; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region XML
                else if (oldValue.Equals(SqlDbType.Xml))
                {
                    // Change Perspective from negation to (the opposite of negation)
                    isSupported = false;
                    switch (newValue)
                    {
                        case SqlDbType.Binary:
                        case SqlDbType.VarBinary:
                        case SqlDbType.Char:
                        case SqlDbType.VarChar:
                        case SqlDbType.NChar:
                        case SqlDbType.NVarChar:
                        case SqlDbType.Xml:
                        case SqlDbType.Udt:
                            isSupported = true; break;
                        default:
                            break;
                    }
                }
                #endregion
                #region UDT
                else if (oldValue.Equals(SqlDbType.Udt))
                {
                    // Change Perspective from negation to (the opposite of negation) 
                    isSupported = false;
                    switch (newValue)
                    {
                        case SqlDbType.Binary:
                        case SqlDbType.VarBinary:
                        case SqlDbType.Char:
                        case SqlDbType.VarChar:
                        case SqlDbType.NChar:
                        case SqlDbType.NVarChar:
                        case SqlDbType.Xml:
                            isSupported = true; break;
                        default:
                            break;
                    }
                }
                #endregion
                return isSupported;
            }
        }
    }
}
